﻿#region using
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
#endregion using

namespace Jsil.Runtime
{
	#region something old
	/*
	public class Executor2
	{
		GlobalObject _global;
		MethodInfo _mi;
		Action _act;
		//__SysGlobalResult _res=new __SysGlobalResult();

		public Executor2(Assembly toExecute)
		{
			Type type = toExecute.GetType("MyType");
			_mi = type.GetMethod("Main", BindingFlags.Public | BindingFlags.Instance);
			_global=(GlobalObject)Activator.CreateInstance(type);
			_act=(Action)Delegate.CreateDelegate(typeof(Action),_global,_mi);
			//_global.ExternalVariables.Add("__SysGlobalResult", _res);
		}

		public IDictionary<string,object> ExternalVariables
		{
			get { return _global.ExternalVariables; }
		}

		public void /object/ Execute()
		{
			_act();
			//_mi.Invoke(_global,new object[0]);
			//return _res.ExecResult;
		}
	}
	*/

	/*class __SysGlobalResult
	{
		public void Set(object value)
		{
			ExecResult=value;
		}

		internal object ExecResult {get;set;}
	}*/
	#endregion something old

#if !DebugRuntime
	[DebuggerStepThrough]
#endif
	public class Executor
	{
		object _global;
		MethodInfo _mi;
		Action _act;
		IDictionary<string,object> _externalVariables;

		public Executor(Assembly toExecute)
		{
			Type type=toExecute.GetType("MyType");
			_mi=type.GetMethod("Main", BindingFlags.Public | BindingFlags.Instance);
			_global=Activator.CreateInstance(type);
			_externalVariables=(IDictionary<string,object>)type.GetProperty("ExternalVariables").GetValue(_global,new object[0]);
			_act=(Action)Delegate.CreateDelegate(typeof(Action),_global,_mi);

			/*EventInfo ei=type.GetEvent("ResolveExternalLibrary");
			ei.GetAddMethod().Invoke(_global,new object[] { (Delegate)ei.EventHandlerType.GetConstructors()[0].Invoke(new object[] { this,this.GetType().GetMethod("FireResolveExternalLibrary",BindingFlags.NonPublic|BindingFlags.Instance).MethodHandle.GetFunctionPointer() }) });*/
			PropertyInfo pi=type.GetProperty("ResolveExternalLibrary");
			pi.SetValue(_global,pi.PropertyType.GetConstructors()[0].Invoke(new object[] { this,this.GetType().GetMethod("FireResolveExternalLibrary",BindingFlags.NonPublic|BindingFlags.Instance).MethodHandle.GetFunctionPointer() }),new object[0]);
		}

		public static void RunExe(Assembly toExecute,string[] args)
		{
			Executor asmExec=new Executor(toExecute);
			asmExec.ExternalVariables["args"]=args;
			asmExec.ResolveExternalLibrary+=(sender,e) => { e.Assembly=Assembly.Load(e.Predefinition); };
			asmExec.Execute();
		}

		public IDictionary<string,object> ExternalVariables
		{
			get { return _externalVariables; }
		}
		
		public event ResolveExternalLibraryEventHandler ResolveExternalLibrary;
		void FireResolveExternalLibrary(object sender,ResolveExternalLibraryEventArgs args)
		{
			if (ResolveExternalLibrary!=null)
				ResolveExternalLibrary(sender,args);
		}

		/// <summary>
		/// Don't use with RuntimeMethodsUsage.Copy - type incompatibility runtime's IRuntimeMethodsExtender and copied IRuntimeMethodsExtender
		/// </summary>
		public IRuntimeMethodsExtender RuntimeMethodsExtender
		{
			get { return RuntimeMethods.RuntimeMethodsExtender; }
			set { RuntimeMethods.RuntimeMethodsExtender=value; }
		}

		public void /*object*/ Execute()
		{
			_act();
		}

		CompiledRuntimeInfo _compiledRuntimeInfo;
		public CompiledRuntimeInfo CompiledRuntimeInfo
		{
			get
			{
				if (_compiledRuntimeInfo==null)
					_compiledRuntimeInfo=new CompiledRuntimeInfo(_mi.DeclaringType.Assembly);
				return _compiledRuntimeInfo;
			}
		}
	}

	public class AssemblyRuntimeInfo
	{
		internal Version _version;
		internal Guid _moduleVersionId;
		public Version Version { get { return _version; } }
		public Guid ModuleVersionId { get { return _moduleVersionId; } }

		static AssemblyRuntimeInfo()
		{
			//Assembly asm=typeof(Executor).Assembly;
			Assembly asm=typeof(AssemblyRuntimeInfo).Assembly;
			_current=new AssemblyRuntimeInfo() { _version=asm.GetName().Version,_moduleVersionId=asm.ManifestModule.ModuleVersionId };
		}

		static AssemblyRuntimeInfo _current;
		public static AssemblyRuntimeInfo Current
		{
			get { return _current; }
		}

		public override bool Equals(object obj)
		{
			AssemblyRuntimeInfo other=obj as AssemblyRuntimeInfo;
			return other==null?false:(_version.Equals(other._version)&&_moduleVersionId.Equals(other._moduleVersionId));
		}

		public override int GetHashCode()
		{
			return _version.GetHashCode()^_moduleVersionId.GetHashCode();
		}
	}

	public class CompiledRuntimeInfo:AssemblyRuntimeInfo
	{
		internal CompiledRuntimeInfo(Assembly asm)
		{
			Type type=asm.GetType("CompileRuntimeInfo");
			_moduleVersionId=new Guid(GetFieldRawConstantValue<string>(type,"ModuleVersionId"));
			_version=new Version(GetFieldRawConstantValue<string>(type,"Version"));
			_hasRuntimeCopy=GetFieldRawConstantValue<bool>(type,"RuntimeCopied");
			_equalsCurrent=this.Equals(Current);
		}

		internal bool _hasRuntimeCopy;
		internal bool _equalsCurrent;
		public bool HasRuntimeCopy { get { return _hasRuntimeCopy; } }
		public bool EqualsCurrent { get { return _equalsCurrent; } }

		T GetFieldRawConstantValue<T>(Type type,string fieldName)
		{
			return (T)type.GetField(fieldName).GetRawConstantValue();
		}
	}
}
